<?php
/**
 * 全局公共文件
 *
 * Created		: 2011-10-20
 * Modified		: 2011-10-21
 * @link		: http://www._utopia.com
 * @copyright	: [Qixbo.Com] (c) 2011
 * @version		: 0.1.0
 * @author		: Joseph Chen (jsph.chen@gmail.com)
 */
define('PRODUCT_SERVER', false);
define('DS', DIRECTORY_SEPARATOR);
define('APP_ROOT', dirname(__FILE__) . DS);

$debug = true;

$app_admin	= APP_ROOT . 'Admin'.DS;
$app_base	= APP_ROOT . 'Base'.DS;
$app_cfg	= APP_ROOT . 'Config'.DS;
$app_lang	= APP_ROOT . 'Lang'.DS;
$app_model	= APP_ROOT . 'Mo'.DS;
$app_view	= APP_ROOT . 'View'.DS;
$app_utils	= APP_ROOT . 'Util'.DS;
// 存放各种数据(日志等)的目录
$app_data	= APP_ROOT . 'Data'.DS;
$app_cache_tpl = $app_data.'cache-view'.DS;



// 载入公共配置文件
if (is_file($_cfgFile = APP_ROOT . 'Config' . DS . 'global.cfg.php')) {
	include $_cfgFile;
	unset($_cfgFile);
}
// 设置程序编码
mb_internal_encoding($app_charset);

// 设置当前使用的语言
if (!empty($_GET['lang']) && is_dir($app_lang.$_REQUEST['lang'].DS)) {
	$lang = $_GET['lang'];
} else if (!empty($_COOKIE['lang']) && is_dir($app_lang.$_COOKIE['lang'].DS)) {
	$lang = $_COOKIE['lang'];
}

//模板文件后缀名
$suffix_tpl = '.html.php';

//当前运行程序的路径
$self_dir = dirname($_SERVER['PHP_SELF']);
if ($self_dir == DS) {
	$self_dir = '';
} else {
	$self_dir = substr($self_dir, 1).DS;
}

/**
 * 定义公用函数
 */

// 自动载入类
function __autoload($className)
{
	if (class_exists($className, false)) {
		return true;
	}
	if (false !== strpos($className, '_')) {// 下划线分隔目录和类
		$filename = APP_ROOT.strtr($className, '_', DS).'.class.php';
	} else {
		if (!isset($GLOBALS['classMapping'])) {
			$GLOBALS['classMapping'] = C('classMapping');
		}
		if (isset($GLOBALS['classMapping'][$className])) {
			$filename = $GLOBALS['classMapping'][$className];
		} else if ($filename = addClassMapping($className)) {
			return true;
		} else {
			return false;
		}
	}
	if ($filename && is_file($filename)) {
		require_once $filename;
	} else {
		return false;
	}
}

/**
 * 添加一个类和路径映射关系,并缓存
 * @param $className
 */
function addClassMapping($className) 
{
	$mapping = C('classMapping');
	if (is_file($filename = APP_ROOT.'Base'.DS.$className.'.class.php')) {
		$mapping[$className] = $filename;
	} else if (is_file($filename = APP_ROOT.'Mo'.DS.$className.'.class.php')) {
		$mapping[$className] = $filename;
	} else if (is_file($filename = APP_ROOT.'Util'.DS.$className.'.class.php')) {
		$mapping[$className] = $filename;
	} else {
		return false;
	}
	require_once $filename;
	
	$file = APP_ROOT.'Config'. DS . 'classMapping.cfg.php';
	$ret = Fso::write($file, 'return '.var_export($mapping,true), 'php');
	$GLOBALS['classMapping'] = $mapping;
	return $filename;
}


/**
 * Config
 * 载入一个/多个配置文件 ,并返回配置数据
 * @param $cfgName,.... 配置名
 */
function C($cfgName)
{
	$cfgFile = APP_ROOT.'Config'.DS.$cfgName.'.cfg.php';
	if (is_file($cfgFile))
	{
		$tmp = include $cfgFile;
		return $tmp;
	}
	return null;
}

/**
 * 载入语言文件
 * @param $langName 语言名
 */
function L($langName)
{
	global $GLOBALS;
	$langFile = APP_ROOT . 'Lang' . DS . $GLOBALS['lang'] . DS . $langName . '.lang.php';
	if (is_file($langFile))
	{
		$tmp = include_once $langFile;
		if (is_array($tmp))
		{
			$GLOBALS = $GLOBALS + $tmp;
		}
	}
}

class App
{
	private static $init = 0;
	
	// 控制view是否继续执行
	private static $view = true;
	
	private static $args = array();
	
	private static $firstOutputDebug = true;
	
	// 是否自动输出缓存
	private static $obAutoOutout = true;
	
	private static $postData = array();
	
	// 保存全局配置或者各类模型的配置
	public static $cfg = array();
	
	// 接口编码格式对应的content-type
	private static $CICType = array(
		'COMMON'  => 'text/html',
		'JSON' => 'application/json',
	);

	public static $contentType = '';
	
	// 最后一次操作的错误信息
	private static $error = '';

	public static function init()
	{
		// 开启输出缓存,避免程序bug造成无用信息直接输出
		ob_start();
		session_start();

		if ((isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] == 'application/x-www-form-urlencoded')
				 || (PRODUCT_SERVER && empty($GLOBALS['debug_script']) && PHP_SAPI!='cli')) {
			set_error_handler('App::errorHandler');
			set_exception_handler('App::exceptionHandler');
		}

		if (isset($_GET['contentType']) && isset(self::$CICType[$_GET['contentType']])) {
			self::$contentType = $_GET['contentType'];
		} else {
			self::$contentType = self::$CICType['COMMON'];
		}
		
		// 输出内容格式
		header('content-type:'.self::$contentType.';charset=utf-8');
		
		// 载入默认的全局语言文件
		L('common');
		
		// 连接默认DB服务器
		if (isset($GLOBALS['db_host']) && isset($GLOBALS['db_name'])) {
			Db::init(
				array(
					'host' => $GLOBALS['db_host'],
					'name' => $GLOBALS['db_name'],
					'user' => $GLOBALS['db_user'],
					'pass' => $GLOBALS['db_pass'],
					'charset' => $GLOBALS['db_charset'],
				)
			);
		}
	}
	
	/**
	 * 设置参数
	 * @param array $vars
	 */
	public static function setOptions($vars=array())
	{
		self::$init = 1;
		if (empty($vars))
		{
			$vars = array(
				'app_theme'		=> $GLOBALS['app_theme'],
				'suffix_tpl'	=> $GLOBALS['suffix_tpl'],
				'theme'			=> $GLOBALS['theme'],
				'self_dir'		=> $GLOBALS['self_dir'],
				'app_cache_tpl'	=> $GLOBALS['app_cache_tpl'],
			);
		} else {
			if (!isset($vars['app_theme'])) {
				$vars['app_theme'] = $GLOBALS['app_theme'];
			}
			if (!isset($vars['suffix_tpl'])) {
				$vars['suffix_tpl'] = $GLOBALS['suffix_tpl'];
			}
			if (!isset($vars['theme'])) {
				$vars['theme'] = $GLOBALS['theme'];
			}
			if (!isset($vars['self_dir'])) {
				$vars['self_dir'] = $GLOBALS['self_dir'];
			}
			if (!isset($vars['app_cache_tpl'])) {
				$vars['app_cache_tpl'] = $GLOBALS['app_cache_tpl'];
			}
		}
		if (!isset($vars['script_name'])) {
			$vars['script_name'] = basename($_SERVER['SCRIPT_NAME'], '.php');
		}
		self::$args = $vars;
		Tpl::init($vars);
	}
	
	/**
	 * 显示页面
	 * @return string
	 */
	public static function view()
	{
		if (!self::$view) {
			return true;
		}
		if (!self::$init) {
			self::setOptions();
		}
		$arg = func_get_args();
		if (empty($arg))
		{
			$tpl = self::$args['script_name'];
			$autoHf = false;
		} elseif ($arg[0]===1 || $arg[0]===true) {
			$autoHf = array('header','footer');
			$tpl = isset($arg[1]) ? $arg[1] : self::$args['script_name'];
		} elseif (is_array($arg[0])){
			$autoHf = $arg[0];
			$tpl = isset($arg[1]) ? $arg[1] : self::$args['script_name'];
		} else {
			$autoHf = 0;
			$tpl = (is_string($arg[0])) ? $arg[0] : self::$args['script_name'];
			if (false !== strpos($arg[0], ','))
			{
				$more_tpl = explode(',', $arg[0]);
			} elseif ($arg[0] == 'MESSAGE') {
				$tpl = 'message';
				$GLOBALS['g_msg'] = array(
					'type' => 'Error',
					'desc' => $arg[1],
				);
			}
		}
		global $g_msg;
		if (!isset($GLOBALS['g_msg'])) {
			$GLOBALS['g_msg'] = $g_msg;
		}
		
		extract($GLOBALS);

		if ($autoHf)
		{
			include Tpl::load($autoHf[0]);
			include Tpl::load($tpl);
			include Tpl::load($autoHf[1]);
		} elseif (isset($more_tpl)) {
			foreach ($more_tpl as $tpl)
			{
				include Tpl::load($tpl);
			}
		} else {
			include Tpl::load($tpl);
		}
		if (self::$obAutoOutout) {
			ob_end_flush();
		}
		return true;
	}
	
	/**
	 * 生成管理员密码
	 * @param string $password
	 */
	public static function generateAdminPwd($password='')
	{
		$privateKey = String::randStr(6);
		if (empty($password)) {
			$password = String::randStr(12, 15);
		}
		$encodePassword = md5(md5($password).md5($privateKey));
		$cfgFile = APP_ROOT.'Config'.DS.'adminpw.cfg.php';
		include($cfgFile);
		$string = <<<heredoc
<?php
\$admin_name = '$admin_name';
\$admin_password = '$encodePassword';
\$admin_private_key = '$privateKey';
heredoc;
		Fso::write($cfgFile, $string);
		
		return $password;
	}
	
	/**
	 * 修改管理员账号和密码
	 * @param string $password
	 */
	public static function modifyAdmin($username, $password='')
	{
		$privateKey = String::randStr(6);
		$cfgFile = APP_ROOT.'Config'.DS.'adminpw.cfg.php';
		include($cfgFile);
		
		// 空密码不修改
		if (empty($password)) {
			$privateKey = $admin_private_key;
			$encodePassword = $admin_password;
		} else {
			$encodePassword = md5(md5($password).md5($privateKey));
		}
		
		$string = <<<heredoc
<?php
\$admin_name = '$username';
\$admin_password = '$encodePassword';
\$admin_private_key = '$privateKey';
heredoc;
		Fso::write($cfgFile, $string);
		
		return $password;
	}
	
	/**
	 * 验证管理员密码是否正确
	 * @param string $password
	 */
	public static function checkAdmin($adminName, $password) 
	{
		$cfgFile = APP_ROOT.'Config'.DS.'adminpw.cfg.php';
		include($cfgFile);
		if ($adminName != $admin_name) {
			App::msg($GLOBALS['LAdminNameError']);
			return false;
		}
		$encodePassword = md5(md5($password).md5($admin_private_key));
		if ($encodePassword == $admin_password) {
			$_SESSION['is_admin'] = true;
			return true;
		} else {
			App::msg($GLOBALS['LAdminPasswordError']);
			return false;
		}
	}
	
	/**
	 * 修改SMTP账号信息
	 * @param string $password
	 */
	public static function smtpSet()
	{
		$cfgFile = APP_ROOT.'Config'.DS.'email.cfg.php';
		include($cfgFile);
		if (empty($_POST['smtp_ssl']) || empty($_POST['smtp_host']) 
				|| empty($_POST['smtp_port']) || empty($_POST['smtp_fromname']) 
				|| empty($_POST['smtp_email']) || empty($_POST['smtp_user'])
				|| empty($_POST['smtp_pass']) || empty($_POST['subjectTpl'])
				|| empty($_POST['bodyTpl'])
		) {
			return false;
		}
		$string = <<<heredoc
<?php
\$smtp_ssl = '{$_POST['smtp_ssl']}';
\$smtp_host = '{$_POST['smtp_host']}';
\$smtp_port = '{$_POST['smtp_port']}';
\$smtp_fromname = '{$_POST['smtp_fromname']}';
\$smtp_email = '{$_POST['smtp_email']}';
\$smtp_user = '{$_POST['smtp_user']}';
\$smtp_pass = '{$_POST['smtp_pass']}';
\$subjectTpl = "{$_POST['subjectTpl']}";
\$bodyTpl = "{$_POST['bodyTpl']}";
heredoc;
		Fso::write($cfgFile, $string);
		
		return true;
	}
	
	/**
	 * 获取POST提交的数据
	 */
	public static function getPostData()
	{
		if (self::$postData) {
			return self::$postData;
		}
		if (!empty($_POST)) {
			self::$postData = $_POST;
			return self::$postData;
		}
		if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
			$data = $GLOBALS['HTTP_RAW_POST_DATA'];
		} else {
			$data = file_get_contents('php://input');
		}
		self::$postData = json_decode($data, true);
		return self::$postData;
	}

	/**
	 * 运行JSON格式请求处理
	 * @throws Exception
	 */
	public static function runJson()
	{
		// 清空缓冲区,避免程序bug,出现异常字符输出
		ob_clean();
		
		$post = self::getPostData();
		
		JsonPhp::service($post);
		JsonPhp::output();
	}
	
	/**
	 * 获取当前开启的语言列表
	 */
	public static function getOpenedLang() 
	{
		$langList = array();
		foreach ($GLOBALS['lang_version_list'] as $k=>$v) {
			if (in_array($k, $GLOBALS['openedLangs'])) {
				$langList[$k] = $v;
			}
		}
		return $langList;
	}
	
	/**
	 * 写配置数据
	 * @param array	 $data
	 * @param string $cfgName
	 */
	public static function writeCfg($data, $cfgName) 
	{
		$cfgFile = APP_ROOT . 'Config' . DS . $cfgName . '.cfg.php';
		$data = 'return '.var_export($data, true);
		Fso::write($cfgFile, $data, 'php');
		return true;
	}

	/**
	 * 设置/返回错误信息
	 * @param $errors 错误信息
	 */
	public static function msg($error=false)
	{
		if ($error === false) {
			return self::$error;
		} else {
			self::$error = $error;
			return self::$error;
		}
	}

	/**
	 * 调试输出一组数据
	 * @param array $data
	 */
	public static function outputDebug($data, $seqId='')
	{
		if (self::$firstOutputDebug) {
			Fso::write(
				APP_ROOT.'debug'.$seqId.'.txt',
				var_export($data, true)."\n".str_repeat('-', 80)."\n"
			);
			self::$firstOutputDebug = false;
		} else {
			Fso::write(
				APP_ROOT.'debug'.$seqId.'.txt',
				var_export($data, true)."\n".str_repeat('-', 80)."\n",
				'text',
				'a'
			);
		}
	}

	/**
	 * 错误处理
	 * @param int $errno
	 * @param string $error
	 * @param string $errfile
	 * @param int $errline
	 */
	public static function errorHandler($code, $message, $errfile, $errline)
	{
		$die = false;
		switch ($code) {
			case E_ERROR:
			case E_USER_ERROR: // 256
				$errType = 'Error';
				$die = true;
				break;
			case E_WARNING:
			case E_USER_WARNING :
				$errType = 'Warning';
				break;
			case E_CORE_ERROR:
			case E_COMPILE_ERROR:
				$errType = 'Fatal Error: ';
				$die = true;
				break;
			case E_PARSE:
				$errType = 'Parse Error';
				$die = true;
				break;
			case E_NOTICE:
			case E_USER_NOTICE :
				$errType = 'Notice';
				break;
			case E_STRICT:
				$errType = 'Strict Standards';
				break;
			default:
				$errType = 'Unknow Error';
				break;
		}
		if (PRODUCT_SERVER) {
			$error = $message;
		} else {
			$error = new stdClass();
			$error->code = $code;
			$error->type = $errType;
			$error->message = $message;
			$error->file = $errfile;
			$error->line = $errline;
		}

		if ($die) {
			header('Content-Type: application/json;');
			echo json_encode(
					array(
						'result'		=> false,
						'srv_time'		=> $_SERVER['REQUEST_TIME'],
						'user_logined'	=> User::isLogined(),
						'role_logined'	=> Role::isLogined(),
						'error' 		=> $error,
						'msg'			=> $message
					)
				);
			exit;
		} else {
			self::msg($error);
			return false;
		}
	}

	/**
	 * 异常处理
	 * @param object $e
	 */
	public static function exceptionHandler($e)
	{
		ob_end_clean();
		
		if (PRODUCT_SERVER) {
			$error = $e->getMessage();
		} else {
			$error = new stdClass();
			$error->message = $e->getMessage();
			$error->code = $e->getCode();
			$error->file = $e->getFile();
			$error->line = $e->getLine();
			$error->stack = $e->getTraceAsString();
		}

		header('Content-Type:application/json;');
		

		echo json_encode(
				array(
					'result'		=> false,
					'srv_time'		=> $_SERVER['REQUEST_TIME'],
					'user_logined'	=> User::isLogined(),
					'role_logined'	=> Role::isLogined(),
					'error' 		=> $error,
					'msg'			=> $e->getMessage()
				)
			);
		exit;
	}
	
	/**
	 * 记录系统日志
	 * @param string $mo
	 * @param string $content
	 */
	public static function syslog($mo, $content) 
	{
		$data = array(
			'mo'			=> $mo,
			'content'		=> $content,
			'create_time'	=> time()
		);
		$ret = Db::insert($GLOBALS['tbl_pre'].'syslog', $data);
		return $ret;
	}
	
	/**
	 * 写数据到日志文件中
	 * @param string $file 相对于LOG路径的相对路径文件名
	 * @param string $data 要写入日志的数据
	 * @return boolean
	 */
	public static function log ($data, $file='')
	{
		$logDir= APP_ROOT.'Data'.DS.'log'.DS;
		if (!$file) {
			$file = $logDir.date('Ymd/H').'.log';
		} else {
			$file = $logDir.date('Ymd/').$file.'.log';
		}
		$dir = dirname($file);
		if (!is_dir($dir)) {
			mkdir($dir, 0755, true);
		}
		if (is_array($data)) {
			$data = var_export($data, true);
		}
		$data = date('Y-m-d H:i:s')."\n".$data."\n\n================\n\n";
		// 多次尝试文件是否可写,防止文件锁定写入失败
		for ($i=0; $i<5; $i++) {
			if (!file_exists($file)) {
				$fp = fopen($file, 'a+');
			}
			// 文件体积大于10M,重新建立新文件,以"分秒"扩展到原文件名中(不含扩展名)
			elseif ( filesize($file) > 10000000000 ) {
				$filepart = explode('.', $file);
				$file = $filepart[0].date(',is').$filepart[1];
			}
			if (is_writable($file)) {
				if (empty($fp)) {
					$fp = fopen($file, 'a+');
				}
				flock($fp, LOCK_EX); // 文件是我的,谁也不许动
				fwrite($fp, $data); // 迁城记录写入文件
				flock($fp, LOCK_UN); // 用完了,谁爱用谁用去
				fclose($fp);
				break;// 可正常写文件就跳出循环尝试
			}
			if ($i == 4) { // 试了这么多次还不能正常写入? 开辟绿色通道.
				$filename = basename($file);
				$file = $dir.'/'.date('Y-m-d.H-i-s').'-_-'.$filename;
				if ($fp=fopen($file, 'a+')) {
					fwrite($fp, $data); // 记录写入文件
					fclose($fp);
				}
			}
			usleep(10000);//本次不能写,可能被锁定,延时一会儿再尝试
		}
//		debug_print_backtrace();
		return true;
	}
	
}

App::init();









